<?php

namespace Xibeicity\Message;

use Xibeicity\Message\Contracts\MessageInterface;
use Xibeicity\Message\Database\MessageLogger;

class MessageManager
{
    /**
     * 消息驱动实例
     *
     * @var array
     */
    protected $drivers = [];

    /**
     * 默认驱动
     *
     * @var string
     */
    protected $default = 'sms';

    /**
     * 配置信息
     *
     * @var array
     */
    protected $config = [];

    /**
     * 自定义驱动创建器
     *
     * @var array
     */
    protected $customCreators = [];

    /**
     * 消息日志记录器
     *
     * @var MessageLogger|null
     */
    protected $logger = null;

    /**
     * 构造函数
     *
     * @param array $config
     * @param string|null $driver 默认驱动名称
     */
    public function __construct(array $config = [], ?string $driver = null)
    {
        $this->config = $config;

        if ($driver) {
            $this->default = $driver;
        }

        if (isset($config['log']) && $config['log']['enabled']) {
            $this->logger = new MessageLogger($config['log']);
        }
    }

    /**
     * 发送消息
     *
     * @param array $to
     * @param string $content
     * @param array $options
     * @param string|null $driver
     * @return array
     */
    public function send(array $to, string $content, array $options = [], ?string $driver = null): array
    {
        $driver = $driver ?: $this->default;
        $result = $this->driver($driver)->send($to, $content, $options);

        if ($this->logger) {
            $this->logger->log(
                $driver,
                implode(',', $to),
                $content,
                $options,
                $result['success'] ? 'success' : 'failed',
                $result['success'] ? null : $this->driver($driver)->getError()
            );
        }

        return $result;
    }

    /**
     * 使用模板发送消息
     *
     * @param array $to
     * @param string $templateCode
     * @param array $templateParams
     * @param array $options
     * @param string|null $driver
     * @return array
     */
    public function sendTemplate(array $to, string $templateCode, array $templateParams = [], array $options = [], ?string $driver = null): array
    {
        $driver = $driver ?: $this->default;
        $result = $this->driver($driver)->sendTemplate($to, $templateCode, $templateParams, $options);

        if ($this->logger) {
            $this->logger->log(
                $driver,
                implode(',', $to),
                "Template: {$templateCode}",
                array_merge($templateParams, $options),
                $result['success'] ? 'success' : 'failed',
                $result['success'] ? null : $this->driver($driver)->getError()
            );
        }

        return $result;
    }

    /**
     * 批量发送消息
     *
     * @param array $messages
     * @param string|null $driver
     * @return array
     */
    public function batchSend(array $messages, ?string $driver = null): array
    {
        $driver = $driver ?: $this->default;
        $results = $this->driver($driver)->batchSend($messages);

        if ($this->logger) {
            foreach ($messages as $index => $message) {
                $result = $results[$index] ?? ['success' => false];
                $this->logger->log(
                    $driver,
                    implode(',', $message['to']),
                    $message['content'] ?? '',
                    $message['options'] ?? [],
                    $result['success'] ? 'success' : 'failed',
                    $result['success'] ? null : $this->driver($driver)->getError()
                );
            }
        }

        return $results;
    }

    /**
     * 获取发送状态
     *
     * @return array
     */
    public function getStatus(): array
    {
        return $this->driver()->getStatus();
    }

    /**
     * 获取错误信息
     *
     * @return string|null
     */
    public function getError(): ?string
    {
        return $this->driver()->getError();
    }

    /**
     * 获取消息驱动实例
     *
     * @param string|null $name
     * @return MessageInterface
     * @throws \InvalidArgumentException
     */
    public function driver(?string $name = null): MessageInterface
    {
        $name = $name ?: $this->default;

        if (!isset($this->drivers[$name])) {
            $this->drivers[$name] = $this->createDriver($name);
        }

        return $this->drivers[$name];
    }

    /**
     * 创建消息驱动实例
     *
     * @param string $name
     * @return MessageInterface
     * @throws \InvalidArgumentException
     */
    protected function createDriver(string $name): MessageInterface
    {
        if (!isset($this->config[$name])) {
            throw new \InvalidArgumentException("Message driver [{$name}] not configured.");
        }

        $config = $this->config[$name];

        if (isset($this->customCreators[$name])) {
            return $this->customCreators[$name]($config);
        }

        $method = 'create' . ucfirst($name) . 'Driver';

        if (method_exists($this, $method)) {
            return $this->$method($config);
        }

        throw new \InvalidArgumentException("Driver [{$name}] not supported.");
    }

    /**
     * 注册自定义驱动
     *
     * @param string $driver
     * @param \Closure $callback
     * @return $this
     */
    public function extend(string $driver, \Closure $callback)
    {
        $this->customCreators[$driver] = $callback;

        return $this;
    }

    /**
     * 配置驱动
     *
     * @param string $name
     * @param array $config
     * @return $this
     */
    public function configure(string $name, array $config)
    {
        $this->config[$name] = array_merge($this->config[$name] ?? [], $config);

        return $this;
    }

    /**
     * 设置默认驱动
     *
     * @param string $name
     * @return $this
     */
    public function setDefaultDriver(string $name)
    {
        $this->default = $name;

        return $this;
    }

    /**
     * 获取默认驱动名称
     *
     * @return string
     */
    public function getDefaultDriver(): string
    {
        return $this->default;
    }

    /**
     * 动态调用驱动方法
     *
     * @param string $method
     * @param array $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->driver()->$method(...$parameters);
    }
}